
const yibai = {}

!function () {
    // 保存原生的 Function.prototype.toString 方法
    const $toString = Function.prototype.toString
    // 创建一个 Symbol 对象，用于唯一标识自定义的 toString 方法
    const symbol = Symbol()
    // 定义一个新的 toString 方法，如果对象是函数类型，则返回自定义的字符串或原生的 toString 结果
    const myToString = function () {
        return typeof this === "function" && (this[symbol] || $toString.call(this))
    }
    // 设置原生函数对象的属性，使其不可枚举但可配置可写，并赋予新的值
    function set_native(func, key, value) {
        Object.defineProperty(func, key, {
            enumerable: false,
            configurable: true,
            writable: true,
            value: value
        })
    }

    // 删除原生的 Function.prototype.toString 方法
    delete Function.prototype.toString
    // 将自定义的 toString 方法设置为 Function.prototype 的新 toString 方法
    set_native(Function.prototype, "toString", myToString)
    // 使用 symbol 设置一个自定义的函数字符串表示形式
    set_native(Function.prototype.toString, symbol, "function toString() { [native code] }")
    // 在全局对象上添加一个 setNative 方法，用于设置函数对象的自定义 toString 方法
    yibai.setNative = function (func, functionName) {
        set_native(func, symbol, `function ${functionName || func.name || ""}() { [native code] }`)
    }
}()


yibai.reNameFun = function (fun, name) {

    // 定义对象fun的name属性
    Object.defineProperty(fun, "name", {
        value: name,
        writable: false,
        enumerable: false,
        configurable: true
    })
}

//originFunc: 原始函数
//funcInfo: 函数信息
//isDebug: 是否开启调试模式
//beforeFunc: 前置函数
//afterFunc: 后置函数
//isExecOriginFunc: 是否执行原始函数
//返回hook后的原函数
yibai.hookFunction = function (originFunc, funcInfo, isDebug, beforeFunc, afterFunc, isExecOriginFunc) {

    if (typeof originFunc !== "function") {
        return originFunc
    }

    if (funcInfo === undefined) {
        funcInfo = {
            objName: "globalThis",
            funcName: originFunc.name || ""
        }
    }
    if (isDebug) {
        isDebug = false
    }
    if (beforeFunc === undefined) {
        beforeFunc = function (obj) {
            console.log(`[HOOK]${funcInfo.objName}[${funcInfo.funcName}]正在调用,参数是:{${obj.args}}`);
        }
    }
    if (afterFunc === undefined) {
        afterFunc = function (obj) {
            console.log(`[HOOK]${funcInfo.objName}[${funcInfo.funcName}]正在调用,返回值是:{${JSON.stringify(obj.result)}}`);
        }

    }
    if (isExecOriginFunc === undefined) {
        isExecOriginFunc = true
    }

    const hookFunc = function () {

        const obj = {}
        obj.args = Array.prototype.slice.call(arguments)
        if (isDebug) {
            debugger
        }
        beforeFunc.call(this, obj)
        let result
        if (isExecOriginFunc) {
            result = originFunc.apply(this, obj.args)
        }
        obj.result = result
        afterFunc.call(this, obj)
        return obj.result
    }
    yibai.setNative(hookFunc, funcInfo.funcName)
    yibai.reNameFun(hookFunc, funcInfo.funcName)
    return hookFunc
}
//hook对象属性
yibai.hookObject = function (obj, objName, proper, isDebug) {
    //object hook对象
    //objectName hook对象名
    //propertype hook对象属性
    //isDebug 是否开启debugger调试  

    let oldDescriptor = Object.getOwnPropertyDescriptor(obj, proper)
    let newDescriptor = {}
    if (!oldDescriptor.configurable) {
        return
    }
    newDescriptor.configurable == oldDescriptor.configurable
    newDescriptor.enumerable == oldDescriptor.enumerable

    if (oldDescriptor.writable) {
        newDescriptor.writable == oldDescriptor.writable
    }
    if (oldDescriptor.hasOwnProperty("value")) {
        let value = oldDescriptor.value
        if (typeof value !== "function") {
            return
        }
        const funcInfo = {
            objName: objName,
            funcName: proper
        }
        newDescriptor.value = yibai.hookFunction(value, funcInfo, isDebug)
    }
    if (oldDescriptor.hasOwnProperty("get")) {
        let get = oldDescriptor.get
        if (typeof get !== "function") {
            return
        }
        const funcInfo = {
            objName: objName,
            funcName: `get ${proper}`
        }
        newDescriptor.get = yibai.hookFunction(get, funcInfo, isDebug)
    }
    if (oldDescriptor.hasOwnProperty("set")) {
        let set = oldDescriptor.set
        if (typeof set !== "function") {
            return
        }
        const funcInfo = {
            objName: objName,
            funcName: `set ${proper}`
        }
        newDescriptor.set = yibai.hookFunction(set, funcInfo, isDebug)
    }

    Object.defineProperty(obj, proper, newDescriptor)


}

//hook 原型对象的所有属性


yibai.hookPropto = function (obj, isDebug) {
    let objProto = obj.prototype
    let protoObjname = obj.name

    for (const key in Object.getOwnPropertyDescriptors(objProto)) {
        yibai.hookObject(objProto, `${protoObjname}.prototype`, key, isDebug)
    }
    console.log(`hook ${protoObjname}.prototype`);
}




yibai.hookGlobal = function (isDebug) {

    for (const key in Object.getOwnPropertyDescriptors(window)) {

        if (typeof window[key] === "function") {
            if (typeof window[key].prototype === "object") {
                //有原型对象
                yibai.hookPropto(window[key], isDebug)
            }
            if (typeof window[key].prototype === "undefined") {

                // 没有原型对象
                funcInfo = {
                    objName: "globalThis",
                    funcName: key
                }
                yibai.hookFunction(window[key], funcInfo, isDebug)
            }

        }

    }

}

// yibai.hookGlobal()










